import { Drag } from "@/utils/Drag";
import { CustomElement } from "../../CustomElement";
import { GraphNode } from "../../GraphNode";
import { GraphView } from "../../GraphView";
import { KeyHelper } from "@/utils/KeyHelper";


export class BoxSelectionHelp extends CustomElement{
    info = {
        left1: 0,
        top1: 0,
        left2: 0,
        top2: 0,
        width: 0,
        height: 0,
        left: 0,
        top: 0
    }
    
    target: GraphView
    selectGraphNodes: GraphNode[] = []
    selectDrags: Drag[] = []

    operateList: any[] = []

    constructor(target: GraphView){
        super()
        this.target = target
        const move = this.move.bind(this)
        const up = ()=> this.mouseup(move, up)

        target.addEventListener("mousedown", ({offsetX, offsetY, target: t, button}:any) => {
            
            if(t === target.$el && button === 0){
                this.info.left1 = offsetX
                this.info.top1 = offsetY
                this.info.left2 = offsetX
                this.info.top2 = offsetY
                
                target.addEventListener("mousemove", move)
                target.addEventListener("mouseup",up)

                target.appendChild(this)
                this.background("rgba(255,255,255,0.4)")
                this.update()
            }
            else if(button === 2){
                const graphNode = this.getGraphNode(t.that) as GraphNode
                if(graphNode){
                    this.addActive([graphNode])
                }
            }
        })

        target.addEventListener("click", ({target}:any)=>{
            this.resetInfo()
            this.update()
            const graphNode = this.getGraphNode(target.that) as GraphNode
            if(graphNode){
                if(this.selectGraphNodes.indexOf(graphNode) === -1) this.cancelActiveAll()
                this.addActive([graphNode])
            }
        })

        target.addEventListener("dblclick", ({target}:any)=>{
            this.resetInfo()
            this.update()
            const graphNode = this.getGraphNode(target.that) as GraphNode
            if(graphNode){
                if(this.selectGraphNodes.indexOf(graphNode) > -1) this.cancelActiveAll()
                this.addActive([graphNode])
            }
        })

        this.pointerEvents('none').zIndex(100)

        this.target.key.combinationKey("selectAll", [KeyHelper.CTRL, KeyHelper.A], 2)
        this.target.key.addChangeEventListener((k:string, type: string)=>{
            if(this.target.key.isCombinationKey('selectAll')){
                this.addActive(this.target.list)
            }
            
            // 检测是否按下删除键，而且只有一个按键触发，当前触发的是删除键
            if(this.target.key.isKeyDown(KeyHelper.DELETE) && this.target.key.isOneKey() && k === KeyHelper.DELETE){
                this.delete()
            }
        })
        // 阻止浏览器 ctrl + a
        this.target.key.setExcludeDefaultBehavior(()=> this.target.key.isCombinationKey('selectAll'))
    }

    /**
     * 对选中的节点进行删除
     */
    delete() {
        
        this.selectGraphNodes.forEach(node=>{
            node.delete()
        })

        this.cancelActiveAll()
    }

    mouseup(move:any, up: any){
        this.target.removeEventListener("mousemove", move)
        this.target.removeEventListener("mouseup", up)
        this.target.removeChild(this)
        this.background("none")
        this.moveEnd()
    }
    
    resetInfo(){
        this.info = {
            left1: 0,
            top1: 0,
            left2: 0,
            top2: 0,
            width: 0,
            height: 0,
            left: 0,
            top: 0
        }
    }

    addActive(list: GraphNode[]){
        list.forEach( graphNode => {
            if(this.selectGraphNodes.indexOf(graphNode) > -1) return
            const drag = new Drag(graphNode, this.target as CustomElement)
            drag.onMove = (e:any)=> {
                graphNode.onMove(e)
                this.itemMove(graphNode, e)
            }
            this.selectDrags.push( drag )
            this.selectGraphNodes.push( graphNode )
            graphNode.outline("solid 2px #2196f3").zIndex(100)
        })
    }

    cancelActive(index: number | GraphNode, drag?: Drag){
        if(index instanceof GraphNode){
            index.outline("solid 2px transparent").zIndex(1)
            drag?.destory()
        }
        else{
            const graphNode = this.selectGraphNodes[index]
            graphNode.outline("solid 2px transparent").zIndex(1)

            const drag = this.selectDrags[index]
            drag.destory()
        }
    }

    cancelActiveAll(){
        
        for (let i = 0; i < this.selectGraphNodes.length; i++) {
            this.cancelActive(i)
        }

        this.selectGraphNodes.length = 0
        this.selectDrags.length = 0
    }

    moveEnd(){
        this.cancelActiveAll()
        if(this.info.width === 0  || this.info.height === 0) return
        
        const graphView = this.target as GraphView
        const selects:GraphNode[] = []
        graphView.list.forEach(graphNode=>{
            const position = graphNode.getPosition()
            const { width, height } = graphNode.getRect()

            this.isColliding(
                {
                    x: position.left,
                    y: position.top,
                    width,
                    height
                },
                {
                    x: this.info.left,
                    y: this.info.top,
                    width: this.info.width,
                    height: this.info.height
                }
            ) && selects.push(graphNode)
        })

        this.addActive( selects )
    }

    isColliding(rect1:any, rect2:any) {
        return (
            rect1.x < rect2.x + rect2.width &&
            rect1.x + rect1.width > rect2.x &&
            rect1.y < rect2.y + rect2.height &&
            rect1.y + rect1.height > rect2.y
        )
    }

    itemMove(graphNode:GraphNode, e:any){
        this.selectGraphNodes.forEach(itemGraphNode=>{
            if(itemGraphNode !== graphNode){
                const position = itemGraphNode.getPosition()
                itemGraphNode.position("absolute", position.left + e.movementX + 'px', position.top + e.movementY + 'px')
                itemGraphNode.onMove(e)
            }
        })
    }

    getGraphNode(target: CustomElement): CustomElement | null {
        try {
            if(target.isGraphNode) return target
            if(target.parent) return this.getGraphNode( target.parent )
        } catch (error) {
            return null
        }
        
        return null
    }

    move(e:MouseEvent){
        const {target, offsetX, offsetY} = e
        
        const rect = this.target.getRect()
        const targetRect = (target as HTMLDivElement ).getBoundingClientRect()

        this.info.left2 = targetRect.left - rect.left + offsetX
        this.info.top2 = targetRect.top - rect.top + offsetY

        this.update()
    }

    update(){
        const width = Math.abs(this.info.left2 - this.info.left1)
        const height = Math.abs(this.info.top2 - this.info.top1)

        const left = Math.min(this.info.left1, this.info.left2)
        const top = Math.min(this.info.top1, this.info.top2)

        this.position("absolute", left + 'px', top + 'px')

        this.width(width + "px")
        this.height(height + "px")

        this.info.width = width
        this.info.height = height
        this.info.top = top
        this.info.left = left
    }
}